import Base from '@/common/cmdBase/cmdBase';
import { lineManager, LineType, pointsManager } from '@/common/preview';
import { useDocumentStore } from '@/common/store';
import { EntityInfo, EntityPartType, THREE } from '@amcax/web-render';

export default class CmdInteractor extends Base {
  idxList: number[] = []; // 面片索引数组
  pointsList: THREE.Vector3Tuple[] = []; // 点数组
  mouseState: boolean = false; // 当前鼠标状态
  dxidInfo: string | number = null; // 整体网格object id
  setIsFormDataValid: (state: boolean) => void; // 更改确定按钮状态
  formData: any = null; //表单信息

  constructor(setIsFormDataValid: (state: boolean) => void) {
    super();
    this.setIsFormDataValid = setIsFormDataValid;
    this.init();
    this.registerEvents();
  }

  // 初始化
  init() {
    this.interactionMgr.pickType = [
      EntityPartType.element,
      EntityPartType.line,
    ];
    this.interactionMgr.enableBoxSelect = false;
    lineManager().setAssisMgr(this.assisMgr);
    // 打开拾取状态
    this.interactionMgr.enablePick = true;
  }

  // 鼠标左键事件
  onMouseLeft = async (e: EntityInfo) => {
    this.dxidInfo = e.dxid;
    this.mouseState = false;
    this.idxList.push(e.idx);
    this.pointsList.push(this.snapPoint(e, this.camera, this.container));
  };

  /**
   * 在hover之前，判断如果hover的不是element，则能拾取；
   * 如果hover的是element，则只能拾取 workingMeshObject 网格的element
   */
  onBeforeHovered = (e: EntityInfo) => {
    return (
      e.type !== EntityPartType.element ||
      e?.dxid === useDocumentStore.getState().workingMeshObject
    );
  };

  // 鼠标盘旋绘制点
  onHovered = (hoverInfo: EntityInfo) => {
    if (!hoverInfo?.point) {
      return;
    }
    const { idx, type } = hoverInfo;

    // this.interactionMgr.pickAt({x:1,y:1}, [EntityPartType.element]);
    // 绘制点
    if (this.mouseState) {
      this.idxList.push(idx);
      const snapPointData = this.snapPoint(
        hoverInfo,
        this.camera,
        this.container,
      );
      this.pointsList.push(snapPointData);
      if (this.pointsList.length >= 2) {
        // 先清除线，在释放线
        lineManager().clearLine(LineType.CURVE);
        lineManager().createUpdateCurve(this.pointsList, {}, LineType.CURVE);
        this.setIsFormDataValid(true);
      }
      pointsManager().renderMovePoints([snapPointData]);
      // 渲染刷新
      this.mainRenderer.render();
    }
  };

  // 监听window鼠标离开
  onMouseupWindow = () => {
    this.mouseState = false;
  };

  // 鼠标点击事件监听
  mouseEvent = () => {
    this.mainRenderer.container['addEventListener'](
      'mousedown',
      this.onMouseDownPro,
    );
    window.addEventListener('mouseup', this.onMouseupWindow);
    window.addEventListener('keyup', this.onKeyup);
  };

  // 键盘监听事件
  onKeyup = (ev: KeyboardEvent) => {
    if (ev.ctrlKey && ev.key == 'z') {
      // ctrl+z 撤销
      this.pointsList = [];
      this.idxList = [];
      this.mouseState = false;
      const curve = lineManager().getLine(LineType.CURVE);
      if (curve) {
        lineManager().clearLine(LineType.CURVE);
      }
      this.setIsFormDataValid(false);
      // 渲染刷新
      this.mainRenderer.render();
    }
  };

  // 鼠标点击事件
  onMouseDownPro = () => {
    // 点击重新绘制线时候清除点信息和线的数据
    this.idxList = [];
    this.pointsList = [];
    this.mouseState = true;
    const curve = lineManager().getLine(LineType.CURVE);
    if (curve) {
      lineManager().clearLine(LineType.CURVE);
    }
    this.setIsFormDataValid(false);
  };

  // 释放资源
  release = () => {
    this.clear();
    this.pointsList = [];
    this.mouseState = false;
    const curve = lineManager().getLine(LineType.CURVE);
    if (curve) {
      lineManager().clearLine(LineType.CURVE);
    }
    this.setIsFormDataValid(false);
  };

  // 吸附点（包含世界坐标到屏幕坐标转换）
  snapPoint = (e: EntityInfo, camera: THREE.Camera, container: HTMLElement) => {
    const { type, dxid, idx, point } = e;
    let pointData = point;
    if (type === EntityPartType.line) {
      console.log('=======吸附点==========', { dxid, type, idx });
      // 获取线的点数据信息
      const lineData = this.meshMgr.getEntityData(dxid, type, idx);
      if (lineData?.length > 0) {
        // 转换为二维数组
        const resultArray = [];
        for (let i = 0; i < lineData.length; i += 3) {
          resultArray.push([
            lineData[i], // x
            lineData[i + 1], // y
            lineData[i + 2], // z
          ]);
        }

        // 设置距离阈值（屏幕像素单位）
        const screenThreshold = 10; // 屏幕空间阈值（像素）

        // 转换目标点到屏幕坐标
        const targetScreenPos = new THREE.Vector3(...point)
          .project(camera)
          .clone();
        // 归一化到屏幕像素坐标
        targetScreenPos.x =
          (targetScreenPos.x * 0.5 + 0.5) * container.clientWidth;
        targetScreenPos.y =
          (1 - (targetScreenPos.y * 0.5 + 0.5)) * container.clientHeight;

        let closestPoint = null;
        let minScreenDistance = Infinity;

        // 只检查第一个和最后一个点
        const checkPoints = [
          resultArray[0],
          resultArray[resultArray.length - 1],
        ];

        for (const checkPoint of checkPoints) {
          const worldPos = new THREE.Vector3(...checkPoint);
          // 转换为屏幕坐标
          const screenPos = worldPos.clone().project(camera);
          screenPos.x = (screenPos.x * 0.5 + 0.5) * container.clientWidth;
          screenPos.y =
            (1 - (screenPos.y * 0.5 + 0.5)) * container.clientHeight;

          // 计算屏幕空间距离（忽略Z轴）
          const screenDistance = Math.sqrt(
            Math.pow(screenPos.x - targetScreenPos.x, 2) +
              Math.pow(screenPos.y - targetScreenPos.y, 2),
          );

          // 同时检查世界空间距离（可选）
          const worldDistance = worldPos.distanceTo(
            new THREE.Vector3(...point),
          );

          if (
            screenDistance < screenThreshold &&
            screenDistance < minScreenDistance
          ) {
            closestPoint = worldPos.toArray();
            minScreenDistance = screenDistance;
            console.log(
              `点 ${checkPoint} 屏幕距离 ${screenDistance.toFixed(2)}px < ${screenThreshold}px | ` +
                `世界距离 ${worldDistance.toFixed(2)}`,
            );
          }
        }

        if (closestPoint) {
          pointData = closestPoint;
          console.log('找到最近吸附点:', closestPoint);
        }
      }
    }
    return pointData;
  };

  // 获取点的信息
  getDataInfo = () => {
    return {
      fidx: this.idxList,
      point: this.pointsList,
      dxid: this.dxidInfo,
    };
  };
}
